/**************************************************************************
*  Black Knight - Chess Engine                                            *
*  Copyright (C) 2009  Victor Rusu                                        *
*                                                                         *
*  This program is free software: you can redistribute it and/or modify   *
*  it under the terms of the GNU General Public License as published by   *
*  the Free Software Foundation, either version 3 of the License, or      *
*  (at your option) any later version.                                    *
*                                                                         *
*  This program is distributed in the hope that it will be useful,        *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the           *
*  GNU General Public License for more details.                           *
*                                                                         *
*  You should have received a copy of the GNU General Public License      *
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
**************************************************************************/

#include "logic/engine.h"

#include <string>
#include <sstream>
#include <boost/thread/mutex.hpp>
#include "io/xboard.h"
#include "logic/position.h"

Engine* Engine::engine_instance_ = NULL;

Engine::Engine(std::string name, double version, std::string author,
  Algorithm* algorithm) : kEngineName(name), kEngineVersion(version),
  kEngineAuthor(author), algorithm_(algorithm) {
    current_position_ = NULL;
    idle_mode_ = true;
}

Engine::~Engine() {
    delete current_position_;
}

void Engine::setup(std::string name, double version, std::string author,
  Algorithm* algorithm) {
    engine_instance_ = new Engine(name, version, author, algorithm);
}

Engine* Engine::instance() {
    return engine_instance_;
}

void Engine::check_game_state() {
    if (current_position_->is_checked_mate()) {
        if (current_position_->to_move() == Position::black)
            send_command("1-0 {White mates}");
        else
            send_command("0-1 {Black mates}");
        idle_mode_ = true;
        return;
    }
    if (current_position_->is_stale_mate()) {
        send_command("1/2-1/2 {Stalemate}");
        idle_mode_ = true;
        return;
    }
}

void Engine::main_loop() {
    while (true) {
        if (!idle_mode_) {
            check_game_state();
            if (!idle_mode_ && current_position_->to_move() == engine_side_)
                move();
        }
        check_commands();
    }
}

void Engine::check_commands() {
    Xboard::instance()->lock();
    std::string command;
    command = Xboard::instance()->get_command();
    Xboard::instance()->unlock();

    if (command == "") return;
    //std::cerr << "Engine got command!" << std::endl;

    std::istringstream command_line(command);
    std::string command_name;

    command_line >> command_name;
    if (command_name == "new") {
        new_game();
    } else if (command_name == "level") {
        command_line >> time_control_moves_ >> time_control_length_ >> time_control_increment_;
        clock_[Position::white] = clock_[Position::black] = time_control_length_ * 60 * 100;
    } else if (command_name == "force") {
        idle_mode_ = true;
    } else if (command_name == "playother") {
        engine_side_ = (Position::side)!engine_side_;
    } else if (command_name == "white") {
        engine_side_ = Position::white;
    } else if (command_name == "black") {
        engine_side_ = Position::black;
    } else if (command_name == "time") {
        int time;
        command_line >> time;
        clock_[engine_side_] = time;
    } else if (command_name == "otim") {
        int time;
        command_line >> time;
        clock_[!engine_side_] = time;
    } else if (is_possible_move(command)) {
        if (current_position_ != NULL &&
          current_position_->is_valid_move(command)) {
            current_position_->move(command);
        } else {
            send_command("Illegal move: " + command);
        }
    } else {
        std::cerr << "Engine ignored command: " << command_name << "!" << std::endl;
        //for (int j = Position::kBoardSize - 1; j >= 0; --j) {
        //    for (int i = 0; i < Position::kBoardSize; ++i)
        //        std::cout << current_position_->is_attacked(Position::black, i, j);
        //    std::cout << std::endl;
        //}
        //std::vector<std::string> v = current_position_->get_valid_moves(Position::white);
        //std::cout << "Am trecut de comenzi!" << std::endl;
        //for (int i = 0; i < (int)v.size(); ++i) {
        //    std::cerr << v[i] << std::endl;
        //}

        //std::cout << "White is checked: " << current_position_->is_checked(Position::white) << std::endl;
        //std::cerr << clock_[engine_side_] << " -- " << clock_[!engine_side_] << std::endl;
    }
}

void Engine::check_time() {
}

void Engine::new_game() {
    delete current_position_;
    current_position_ = new Position();
    engine_side_ = Position::black;
    idle_mode_ = false;
}

bool Engine::is_possible_move(std::string move) {
    if (4 > (int)move.size() || (int)move.size() > 5)
        return false;
    if (!('a' <= move[0] && move[0] <= 'h'))
        return false;
    if (!('1' <= move[1] && move[1] <= '8'))
        return false;
    if (!('a' <= move[2] && move[2] <= 'h'))
        return false;
    if (!('1' <= move[3] && move[3] <= '8'))
        return false;
    if ((int)move.size() == 5 &&
      !(move[4] == 'q' || move[4] == 'r' || move[4] == 'n' || move[4] == 'b'))
        return false;
    return true;
}

void Engine::move() {
    std::string my_move = algorithm_->move(*current_position_);
    current_position_->move(my_move);
    my_move = "move " + my_move;
    send_command(my_move);
}

void Engine::send_command(std::string command) {
    Xboard::instance()->lock();
    Xboard::instance()->send_command(command);
    Xboard::instance()->unlock();
}
